1/* $NetBSD: vstream.c,v 1.2 2017/02/14 01:16:49 christos Exp $ */
2
3/*++
4/* NAME
5/* vstream 3
6/* SUMMARY
7/* light-weight buffered I/O package
8/* SYNOPSIS
9/* #include <vstream.h>
10/*
11/* VSTREAM *vstream_fopen(path, flags, mode)
12/* const char *path;
13/* int flags;
14/* mode_t mode;
15/*
16/* VSTREAM *vstream_fdopen(fd, flags)
17/* int fd;
18/* int flags;
19/*
20/* int vstream_fclose(stream)
21/* VSTREAM *stream;
22/*
23/* int vstream_fdclose(stream)
24/* VSTREAM *stream;
25/*
26/* VSTREAM *vstream_printf(format, ...)
27/* const char *format;
28/*
29/* VSTREAM *vstream_fprintf(stream, format, ...)
30/* VSTREAM *stream;
31/* const char *format;
32/*
33/* int VSTREAM_GETC(stream)
34/* VSTREAM *stream;
35/*
36/* int VSTREAM_PUTC(ch, stream)
37/* int ch;
38/*
39/* int VSTREAM_GETCHAR(void)
40/*
41/* int VSTREAM_PUTCHAR(ch)
42/* int ch;
43/*
44/* int vstream_ungetc(stream, ch)
45/* VSTREAM *stream;
46/* int ch;
47/*
48/* int vstream_fputs(str, stream)
49/* const char *str;
50/* VSTREAM *stream;
51/*
52/* off_t vstream_ftell(stream)
53/* VSTREAM *stream;
54/*
55/* off_t vstream_fseek(stream, offset, whence)
56/* VSTREAM *stream;
57/* off_t offset;
58/* int whence;
59/*
60/* int vstream_fflush(stream)
61/* VSTREAM *stream;
62/*
63/* int vstream_fpurge(stream, direction)
64/* VSTREAM *stream;
65/* int direction;
66/*
67/* ssize_t vstream_fread(stream, buf, len)
68/* VSTREAM *stream;
69/* void *buf;
70/* ssize_t len;
71/*
72/* ssize_t vstream_fwrite(stream, buf, len)
73/* VSTREAM *stream;
74/* const void *buf;
75/* ssize_t len;
76/*
77/* void vstream_control(stream, name, ...)
78/* VSTREAM *stream;
79/* int name;
80/*
81/* int vstream_fileno(stream)
82/* VSTREAM *stream;
83/*
84/* const ssize_t vstream_req_bufsize(stream)
85/* VSTREAM *stream;
86/*
87/* void *vstream_context(stream)
88/* VSTREAM *stream;
89/*
90/* int vstream_ferror(stream)
91/* VSTREAM *stream;
92/*
93/* int vstream_ftimeout(stream)
94/* VSTREAM *stream;
95/*
96/* int vstream_feof(stream)
97/* VSTREAM *stream;
98/*
99/* int vstream_clearerr(stream)
100/* VSTREAM *stream;
101/*
102/* const char *VSTREAM_PATH(stream)
103/* VSTREAM *stream;
104/*
105/* char *vstream_vprintf(format, ap)
106/* const char *format;
107/* va_list *ap;
108/*
109/* char *vstream_vfprintf(stream, format, ap)
110/* VSTREAM *stream;
111/* const char *format;
112/* va_list *ap;
113/*
114/* ssize_t vstream_bufstat(stream, command)
115/* VSTREAM *stream;
116/* int command;
117/*
118/* ssize_t vstream_peek(stream)
119/* VSTREAM *stream;
120/*
121/* const char *vstream_peek_data(stream)
122/* VSTREAM *stream;
123/*
124/* int vstream_setjmp(stream)
125/* VSTREAM *stream;
126/*
127/* void vstream_longjmp(stream, val)
128/* VSTREAM *stream;
129/* int val;
130/*
131/* time_t vstream_ftime(stream)
132/* VSTREAM *stream;
133/*
134/* struct timeval vstream_ftimeval(stream)
135/* VSTREAM *stream;
136/*
137/* int vstream_rd_error(stream)
138/* VSTREAM *stream;
139/*
140/* int vstream_wr_error(stream)
141/* VSTREAM *stream;
142/*
143/* int vstream_rd_timeout(stream)
144/* VSTREAM *stream;
145/*
146/* int vstream_wr_timeout(stream)
147/* VSTREAM *stream;
148/*
149/* int vstream_fstat(stream, flags)
150/* VSTREAM *stream;
151/* int flags;
152/* DESCRIPTION
153/* The \fIvstream\fR module implements light-weight buffered I/O
154/* similar to the standard I/O routines.
155/*
156/* The interface is implemented in terms of VSTREAM structure
157/* pointers, also called streams. For convenience, three streams
158/* are predefined: VSTREAM_IN, VSTREAM_OUT, and VSTREAM_ERR. These
159/* streams are connected to the standard input, output and error
160/* file descriptors, respectively.
161/*
162/* Although the interface is patterned after the standard I/O
163/* library, there are some major differences:
164/* .IP \(bu
165/* File descriptors are not limited to the range 0..255. This
166/* was reason #1 to write these routines in the first place.
167/* .IP \(bu
168/* The application can switch between reading and writing on
169/* the same stream without having to perform a flush or seek
170/* operation, and can change write position without having to
171/* flush. This was reason #2. Upon position or direction change,
172/* unread input is discarded, and unwritten output is flushed
173/* automatically. Exception: with double-buffered streams, unread
174/* input is not discarded upon change of I/O direction, and
175/* output flushing is delayed until the read buffer must be refilled.
176/* .IP \(bu
177/* A bidirectional stream can read and write with the same buffer
178/* and file descriptor, or it can have separate read/write
179/* buffers and/or file descriptors.
180/* .IP \(bu
181/* No automatic flushing of VSTREAM_OUT upon program exit, or of
182/* VSTREAM_ERR at any time. No unbuffered or line buffered modes.
183/* This functionality may be added when it is really needed.
184/* .PP
185/* vstream_fopen() opens the named file and associates a buffered
186/* stream with it. The \fIpath\fR, \fIflags\fR and \fImode\fR
187/* arguments are passed on to the open(2) routine. The result is
188/* a null pointer in case of problems. The \fIpath\fR argument is
189/* copied and can be looked up with VSTREAM_PATH().
190/*
191/* vstream_fdopen() takes an open file and associates a buffered
192/* stream with it. The \fIflags\fR argument specifies how the file
193/* was opened. vstream_fdopen() either succeeds or never returns.
194/*
195/* vstream_fclose() closes the named buffered stream. The result
196/* is 0 in case of success, VSTREAM_EOF in case of problems.
197/* vstream_fclose() reports the same errors as vstream_ferror().
198/*
199/* vstream_fdclose() leaves the file(s) open but is otherwise
200/* identical to vstream_fclose().
201/*
202/* vstream_fprintf() formats its arguments according to the
203/* \fIformat\fR argument and writes the result to the named stream.
204/* The result is the stream argument. It understands the s, c, d, u,
205/* o, x, X, e, f and g format types, the l modifier, field width and
206/* precision, sign, and padding with zeros or spaces. In addition,
207/* vstream_fprintf() recognizes the %m format specifier and expands
208/* it to the error message corresponding to the current value of the
209/* global \fIerrno\fR variable.
210/*
211/* vstream_printf() performs formatted output to the standard output
212/* stream.
213/*
214/* VSTREAM_GETC() reads the next character from the named stream.
215/* The result is VSTREAM_EOF when end-of-file is reached or if a read
216/* error was detected. VSTREAM_GETC() is an unsafe macro that
217/* evaluates some arguments more than once.
218/*
219/* VSTREAM_GETCHAR() is an alias for VSTREAM_GETC(VSTREAM_IN).
220/*
221/* VSTREAM_PUTC() appends the specified character to the specified
222/* stream. The result is the stored character, or VSTREAM_EOF in
223/* case of problems. VSTREAM_PUTC() is an unsafe macro that
224/* evaluates some arguments more than once.
225/*
226/* VSTREAM_PUTCHAR(c) is an alias for VSTREAM_PUTC(c, VSTREAM_OUT).
227/*
228/* vstream_ungetc() pushes back a character onto the specified stream
229/* and returns the character, or VSTREAM_EOF in case of problems.
230/* It is an error to push back before reading (or immediately after
231/* changing the stream offset via vstream_fseek()). Upon successful
232/* return, vstream_ungetc() clears the end-of-file stream flag.
233/*
234/* vstream_fputs() appends the given null-terminated string to the
235/* specified buffered stream. The result is 0 in case of success,
236/* VSTREAM_EOF in case of problems.
237/*
238/* vstream_ftell() returns the file offset for the specified stream,
239/* -1 if the stream is connected to a non-seekable file.
240/*
241/* vstream_fseek() changes the file position for the next read or write
242/* operation. Unwritten output is flushed. With unidirectional streams,
243/* unread input is discarded. The \fIoffset\fR argument specifies the file
244/* position from the beginning of the file (\fIwhence\fR is SEEK_SET),
245/* from the current file position (\fIwhence\fR is SEEK_CUR), or from
246/* the file end (SEEK_END). The result value is the file offset
247/* from the beginning of the file, -1 in case of problems.
248/*
249/* vstream_fflush() flushes unwritten data to a file that was
250/* opened in read-write or write-only mode.
251/* vstream_fflush() returns 0 in case of success, VSTREAM_EOF in
252/* case of problems. It is an error to flush a read-only stream.
253/* vstream_fflush() reports the same errors as vstream_ferror().
254/*
255/* vstream_fpurge() discards the contents of the stream buffer.
256/* If direction is VSTREAM_PURGE_READ, it discards unread data,
257/* else if direction is VSTREAM_PURGE_WRITE, it discards unwritten
258/* data. In the case of a double-buffered stream, if direction is
259/* VSTREAM_PURGE_BOTH, it discards the content of both the read
260/* and write buffers. vstream_fpurge() returns 0 in case of success,
261/* VSTREAM_EOF in case of problems.
262/*
263/* vstream_fread() and vstream_fwrite() perform unformatted I/O
264/* on the named stream. The result value is the number of bytes
265/* transferred. A short count is returned in case of end-of-file
266/* or error conditions.
267/*
268/* vstream_control() allows the user to fine tune the behavior of
269/* the specified stream. The arguments are a list of macros with
270/* zero or more arguments, terminated with CA_VSTREAM_CTL_END
271/* which has none. The following lists the names and the types
272/* of the corresponding value arguments.
273/* .IP "CA_VSTREAM_CTL_READ_FN(ssize_t (*)(int, void *, size_t, int, void *))"
274/* The argument specifies an alternative for the timed_read(3) function,
275/* for example, a read function that performs decryption.
276/* This function receives as arguments a file descriptor, buffer pointer,
277/* buffer length, timeout value, and the VSTREAM's context value.
278/* A timeout value <= 0 disables the time limit.
279/* This function should return the positive number of bytes transferred,
280/* 0 upon EOF, and -1 upon error with errno set appropriately.
281/* .IP "CA_VSTREAM_CTL_WRITE_FN(ssize_t (*)(int, void *, size_t, int, void *))"
282/* The argument specifies an alternative for the timed_write(3) function,
283/* for example, a write function that performs encryption.
284/* This function receives as arguments a file descriptor, buffer pointer,
285/* buffer length, timeout value, and the VSTREAM's context value.
286/* A timeout value <= 0 disables the time limit.
287/* This function should return the positive number of bytes transferred,
288/* and -1 upon error with errno set appropriately. Instead of -1 it may
289/* also return 0, e.g., upon remote party-initiated protocol shutdown.
290/* .IP "CA_VSTREAM_CTL_CONTEXT(void *)"
291/* The argument specifies application context that is passed on to
292/* the application-specified read/write routines. No copy is made.
293/* .IP "CA_VSTREAM_CTL_PATH(const char *)"
294/* Updates the stored pathname of the specified stream. The pathname
295/* is copied.
296/* .IP "CA_VSTREAM_CTL_DOUBLE (no arguments)"
297/* Use separate buffers for reading and for writing. This prevents
298/* unread input from being discarded upon change of I/O direction.
299/* .IP "CA_VSTREAM_CTL_READ_FD(int)"
300/* The argument specifies the file descriptor to be used for reading.
301/* This feature is limited to double-buffered streams, and makes the
302/* stream non-seekable.
303/* .IP "CA_VSTREAM_CTL_WRITE_FD(int)"
304/* The argument specifies the file descriptor to be used for writing.
305/* This feature is limited to double-buffered streams, and makes the
306/* stream non-seekable.
307/* .IP "CA_VSTREAM_CTL_SWAP_FD(VSTREAM *)"
308/* The argument specifies a VSTREAM pointer; the request swaps the
309/* file descriptor members of the two streams. This feature is limited
310/* to streams that are both double-buffered or both single-buffered.
311/* .IP "CA_VSTREAM_CTL_DUPFD(int)"
312/* The argument specifies a minimum file descriptor value. If
313/* the actual stream's file descriptors are below the minimum,
314/* reallocate the descriptors to the first free value greater
315/* than or equal to the minimum. The VSTREAM_CTL_DUPFD macro
316/* is defined only on systems with fcntl() F_DUPFD support.
317/* .IP "CA_VSTREAM_CTL_WAITPID_FN(int (*)(pid_t, WAIT_STATUS_T *, int))"
318/* A pointer to function that behaves like waitpid(). This information
319/* is used by the vstream_pclose() routine.
320/* .IP "CA_VSTREAM_CTL_TIMEOUT(int)"
321/* The deadline for a descriptor to become readable in case of a read
322/* request, or writable in case of a write request. Specify a value
323/* of 0 to disable deadlines.
324/* .IP "CA_VSTREAM_CTL_EXCEPT (no arguments)"
325/* Enable exception handling with vstream_setjmp() and vstream_longjmp().
326/* This involves allocation of additional memory that normally isn't
327/* used.
328/* .IP "CA_VSTREAM_CTL_BUFSIZE(ssize_t)"
329/* Specify a non-default buffer size for the next read(2) or
330/* write(2) operation, or zero to implement a no-op. Requests
331/* to reduce the buffer size are silently ignored (i.e. any
332/* positive value <= vstream_req_bufsize()). To get a buffer
333/* size smaller than VSTREAM_BUFSIZE, make the VSTREAM_CTL_BUFSIZE
334/* request before the first stream read or write operation
335/* (i.e., vstream_req_bufsize() returns zero). Requests to
336/* change a fixed-size buffer (i.e., VSTREAM_ERR) are not
337/* allowed.
338/*
339/* NOTE: the vstream_*printf() routines may silently expand a
340/* buffer, so that the result of some %letter specifiers can
341/* be written to contiguous memory.
342/* .IP CA_VSTREAM_CTL_START_DEADLINE (no arguments)
343/* Change the VSTREAM_CTL_TIMEOUT behavior, to limit the total
344/* time for all subsequent file descriptor read or write
345/* operations, and recharge the deadline timer.
346/* .IP CA_VSTREAM_CTL_STOP_DEADLINE (no arguments)
347/* Revert VSTREAM_CTL_TIMEOUT behavior to the default, i.e.
348/* a time limit for individual file descriptor read or write
349/* operations.
350/* .PP
351/* vstream_fileno() gives access to the file handle associated with
352/* a buffered stream. With streams that have separate read/write
353/* file descriptors, the result is the current descriptor.
354/*
355/* vstream_req_bufsize() returns the buffer size that will be
356/* used for the next read(2) or write(2) operation on the named
357/* stream. A zero result means that the next read(2) or write(2)
358/* operation will use the default buffer size (VSTREAM_BUFSIZE).
359/*
360/* vstream_context() returns the application context that is passed on to
361/* the application-specified read/write routines.
362/*
363/* VSTREAM_PATH() is an unsafe macro that returns the name stored
364/* with vstream_fopen() or with vstream_control(). The macro is
365/* unsafe because it evaluates some arguments more than once.
366/*
367/* vstream_feof() returns non-zero when a previous operation on the
368/* specified stream caused an end-of-file condition.
369/* Although further read requests after EOF may complete
370/* succesfully, vstream_feof() will keep returning non-zero
371/* until vstream_clearerr() is called for that stream.
372/*
373/* vstream_ferror() returns non-zero when a previous operation on the
374/* specified stream caused a non-EOF error condition, including timeout.
375/* After a non-EOF error on a stream, no I/O request will
376/* complete until after vstream_clearerr() is called for that stream.
377/*
378/* vstream_ftimeout() returns non-zero when a previous operation on the
379/* specified stream caused a timeout error condition. See
380/* vstream_ferror() for error persistence details.
381/*
382/* vstream_clearerr() resets the timeout, error and end-of-file indication
383/* of the specified stream, and returns no useful result.
384/*
385/* vstream_vfprintf() provides an alternate interface
386/* for formatting an argument list according to a format string.
387/*
388/* vstream_vprintf() provides a similar alternative interface.
389/*
390/* vstream_bufstat() provides input and output buffer status
391/* information. The command is one of the following:
392/* .IP VSTREAM_BST_IN_PEND
393/* Return the number of characters that can be read without
394/* refilling the read buffer.
395/* .IP VSTREAM_BST_OUT_PEND
396/* Return the number of characters that are waiting in the
397/* write buffer.
398/* .PP
399/* vstream_peek() returns the number of characters that can be
400/* read from the named stream without refilling the read buffer.
401/* This is an alias for vstream_bufstat(stream, VSTREAM_BST_IN_PEND).
402/*
403/* vstream_peek_data() returns a pointer to the unread bytes
404/* that exist according to vstream_peek(), or null if no unread
405/* bytes are available.
406/*
407/* vstream_setjmp() saves processing context and makes that context
408/* available for use with vstream_longjmp(). Normally, vstream_setjmp()
409/* returns zero. A non-zero result means that vstream_setjmp() returned
410/* through a vstream_longjmp() call; the result is the \fIval\fR argument
411/* given to vstream_longjmp().
412/*
413/* NB: non-local jumps such as vstream_longjmp() are not safe
414/* for jumping out of any routine that manipulates VSTREAM data.
415/* longjmp() like calls are best avoided in signal handlers.
416/*
417/* vstream_ftime() returns the time of initialization, the last buffer
418/* fill operation, or the last buffer flush operation for the specified
419/* stream. This information is maintained only when stream timeouts are
420/* enabled.
421/*
422/* vstream_ftimeval() is like vstream_ftime() but returns more
423/* detail.
424/*
425/* vstream_rd_mumble() and vstream_wr_mumble() report on
426/* read and write error conditions, respectively.
427/*
428/* vstream_fstat() queries stream status information about
429/* user-requested features. The \fIflags\fR argument is the
430/* bitwise OR of one or more of the following, and the result
431/* value is the bitwise OR of the features that are activated.
432/* .IP VSTREAM_FLAG_DEADLINE
433/* The deadline feature is activated.
434/* .IP VSTREAM_FLAG_DOUBLE
435/* The double-buffering feature is activated.
436/* DIAGNOSTICS
437/* Panics: interface violations. Fatal errors: out of memory.
438/* SEE ALSO
439/* timed_read(3) default read routine
440/* timed_write(3) default write routine
441/* vbuf_print(3) formatting engine
442/* setjmp(3) non-local jumps
443/* BUGS
444/* Should use mmap() on reasonable systems.
445/* LICENSE
446/* .ad
447/* .fi
448/* The Secure Mailer license must be distributed with this software.
449/* AUTHOR(S)
450/* Wietse Venema
451/* IBM T.J. Watson Research
452/* P.O. Box 704
453/* Yorktown Heights, NY 10598, USA
454/*--*/
455
456/* System library. */
457
458#include <sys_defs.h>
459#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
460#include <stdarg.h>
461#include <stddef.h>
462#include <unistd.h>
463#include <fcntl.h>
464#include <time.h>
465#include <errno.h>
466#include <string.h>
467
468/* Utility library. */
469
470#include "mymalloc.h"
471#include "msg.h"
472#include "vbuf_print.h"
473#include "iostuff.h"
474#include "vstring.h"
475#include "vstream.h"
476
477/* Application-specific. */
478
479 /*
480 * Forward declarations.
481 */
482static int vstream_buf_get_ready(VBUF *);
483static int vstream_buf_put_ready(VBUF *);
484static int vstream_buf_space(VBUF *, ssize_t);
485
486 /*
487 * Initialization of the three pre-defined streams. Pre-allocate a static
488 * I/O buffer for the standard error stream, so that the error handler can
489 * produce a diagnostic even when memory allocation fails.
490 */
491static unsigned char vstream_fstd_buf[VSTREAM_BUFSIZE];
492
493VSTREAM vstream_fstd[] = {
494 {{
495 0, /* flags */
496 0, 0, 0, 0, /* buffer */
497 vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
498 }, STDIN_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
499 0,},
500 {{
501 0, /* flags */
502 0, 0, 0, 0, /* buffer */
503 vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
504 }, STDOUT_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
505 0,},
506 {{
507 VBUF_FLAG_FIXED | VSTREAM_FLAG_WRITE,
508 vstream_fstd_buf, VSTREAM_BUFSIZE, VSTREAM_BUFSIZE, vstream_fstd_buf,
509 vstream_buf_get_ready, vstream_buf_put_ready, vstream_buf_space,
510 }, STDERR_FILENO, (VSTREAM_RW_FN) timed_read, (VSTREAM_RW_FN) timed_write,
511 VSTREAM_BUFSIZE,},
512};
513
514#define VSTREAM_STATIC(v) ((v) >= VSTREAM_IN && (v) <= VSTREAM_ERR)
515
516 /*
517 * A bunch of macros to make some expressions more readable. XXX We're
518 * assuming that O_RDONLY == 0, O_WRONLY == 1, O_RDWR == 2.
519 */
520#define VSTREAM_ACC_MASK(f) ((f) & (O_APPEND | O_WRONLY | O_RDWR))
521
522#define VSTREAM_CAN_READ(f) (VSTREAM_ACC_MASK(f) == O_RDONLY \
523 || VSTREAM_ACC_MASK(f) == O_RDWR)
524#define VSTREAM_CAN_WRITE(f) (VSTREAM_ACC_MASK(f) & O_WRONLY \
525 || VSTREAM_ACC_MASK(f) & O_RDWR \
526 || VSTREAM_ACC_MASK(f) & O_APPEND)
527
528#define VSTREAM_BUF_COUNT(bp, n) \
529 ((bp)->flags & VSTREAM_FLAG_READ ? -(n) : (n))
530
531#define VSTREAM_BUF_AT_START(bp) { \
532 (bp)->cnt = VSTREAM_BUF_COUNT((bp), (bp)->len); \
533 (bp)->ptr = (bp)->data; \
534 }
535
536#define VSTREAM_BUF_AT_OFFSET(bp, offset) { \
537 (bp)->ptr = (bp)->data + (offset); \
538 (bp)->cnt = VSTREAM_BUF_COUNT(bp, (bp)->len - (offset)); \
539 }
540
541#define VSTREAM_BUF_AT_END(bp) { \
542 (bp)->cnt = 0; \
543 (bp)->ptr = (bp)->data + (bp)->len; \
544 }
545
546#define VSTREAM_BUF_ZERO(bp) { \
547 (bp)->flags = 0; \
548 (bp)->data = (bp)->ptr = 0; \
549 (bp)->len = (bp)->cnt = 0; \
550 }
551
552#define VSTREAM_BUF_ACTIONS(bp, get_action, put_action, space_action) { \
553 (bp)->get_ready = (get_action); \
554 (bp)->put_ready = (put_action); \
555 (bp)->space = (space_action); \
556 }
557
558#define VSTREAM_SAVE_STATE(stream, buffer, filedes) { \
559 stream->buffer = stream->buf; \
560 stream->filedes = stream->fd; \
561 }
562
563#define VSTREAM_RESTORE_STATE(stream, buffer, filedes) do { \
564 stream->buffer.flags = stream->buf.flags; \
565 stream->buf = stream->buffer; \
566 stream->fd = stream->filedes; \
567 } while(0)
568
569#define VSTREAM_FORK_STATE(stream, buffer, filedes) { \
570 stream->buffer = stream->buf; \
571 stream->filedes = stream->fd; \
572 stream->buffer.data = stream->buffer.ptr = 0; \
573 stream->buffer.len = stream->buffer.cnt = 0; \
574 stream->buffer.flags &= ~VSTREAM_FLAG_FIXED; \
575 };
576
577#define VSTREAM_FLAG_READ_DOUBLE (VSTREAM_FLAG_READ | VSTREAM_FLAG_DOUBLE)
578#define VSTREAM_FLAG_WRITE_DOUBLE (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_DOUBLE)
579
580#define VSTREAM_FFLUSH_SOME(stream) \
581 vstream_fflush_some((stream), (stream)->buf.len - (stream)->buf.cnt)
582
583/* Note: this does not change a negative result into a zero result. */
584#define VSTREAM_SUB_TIME(x, y, z) \
585 do { \
586 (x).tv_sec = (y).tv_sec - (z).tv_sec; \
587 (x).tv_usec = (y).tv_usec - (z).tv_usec; \
588 while ((x).tv_usec < 0) { \
589 (x).tv_usec += 1000000; \
590 (x).tv_sec -= 1; \
591 } \
592 while ((x).tv_usec >= 1000000) { \
593 (x).tv_usec -= 1000000; \
594 (x).tv_sec += 1; \
595 } \
596 } while (0)
597
598/* vstream_buf_init - initialize buffer */
599
600static void vstream_buf_init(VBUF *bp, int flags)
601{
602
603 /*
604 * Initialize the buffer such that the first data access triggers a
605 * buffer boundary action.
606 */
607 VSTREAM_BUF_ZERO(bp);
608 VSTREAM_BUF_ACTIONS(bp,
609 VSTREAM_CAN_READ(flags) ? vstream_buf_get_ready : 0,
610 VSTREAM_CAN_WRITE(flags) ? vstream_buf_put_ready : 0,
611 vstream_buf_space);
612}
613
614/* vstream_buf_alloc - allocate buffer memory */
615
616static void vstream_buf_alloc(VBUF *bp, ssize_t len)
617{
618 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
619 ssize_t used = bp->ptr - bp->data;
620 const char *myname = "vstream_buf_alloc";
621
622 if (len < bp->len)
623 msg_panic("%s: attempt to shrink buffer", myname);
624 if (bp->flags & VSTREAM_FLAG_FIXED)
625 msg_panic("%s: unable to extend fixed-size buffer", myname);
626
627 /*
628 * Late buffer allocation allows the user to override the default policy.
629 * If a buffer already exists, allow for the presence of (output) data.
630 */
631 bp->data = (unsigned char *)
632 (bp->data ? myrealloc((void *) bp->data, len) : mymalloc(len));
633 bp->len = len;
634 if (bp->flags & VSTREAM_FLAG_READ) {
635 bp->ptr = bp->data + used;
636 if (bp->flags & VSTREAM_FLAG_DOUBLE)
637 VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
638 } else {
639 VSTREAM_BUF_AT_OFFSET(bp, used);
640 if (bp->flags & VSTREAM_FLAG_DOUBLE)
641 VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
642 }
643}
644
645/* vstream_buf_wipe - reset buffer to initial state */
646
647static void vstream_buf_wipe(VBUF *bp)
648{
649 if ((bp->flags & VBUF_FLAG_FIXED) == 0 && bp->data)
650 myfree((void *) bp->data);
651 VSTREAM_BUF_ZERO(bp);
652 VSTREAM_BUF_ACTIONS(bp, 0, 0, 0);
653}
654
655/* vstream_fflush_some - flush some buffered data */
656
657static int vstream_fflush_some(VSTREAM *stream, ssize_t to_flush)
658{
659 const char *myname = "vstream_fflush_some";
660 VBUF *bp = &stream->buf;
661 ssize_t used;
662 ssize_t left_over;
663 void *data;
664 ssize_t len;
665 ssize_t n;
666 int timeout;
667 struct timeval before;
668 struct timeval elapsed;
669
670 /*
671 * Sanity checks. It is illegal to flush a read-only stream. Otherwise,
672 * if there is buffered input, discard the input. If there is buffered
673 * output, require that the amount to flush is larger than the amount to
674 * keep, so that we can memcpy() the residue.
675 */
676 if (bp->put_ready == 0)
677 msg_panic("%s: read-only stream", myname);
678 switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
679 case VSTREAM_FLAG_READ: /* discard input */
680 VSTREAM_BUF_AT_END(bp);
681 /* FALLTHROUGH */
682 case 0: /* flush after seek? */
683 return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
684 case VSTREAM_FLAG_WRITE: /* output buffered */
685 break;
686 case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
687 msg_panic("%s: read/write stream", myname);
688 }
689 used = bp->len - bp->cnt;
690 left_over = used - to_flush;
691
692 if (msg_verbose > 2 && stream != VSTREAM_ERR)
693 msg_info("%s: fd %d flush %ld", myname, stream->fd, (long) to_flush);
694 if (to_flush < 0 || left_over < 0)
695 msg_panic("%s: bad to_flush %ld", myname, (long) to_flush);
696 if (to_flush < left_over)
697 msg_panic("%s: to_flush < left_over", myname);
698 if (to_flush == 0)
699 return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
700 if (bp->flags & VSTREAM_FLAG_ERR)
701 return (VSTREAM_EOF);
702
703 /*
704 * When flushing a buffer, allow for partial writes. These can happen
705 * while talking to a network. Update the cached file seek position, if
706 * any.
707 *
708 * When deadlines are enabled, we count the elapsed time for each write
709 * operation instead of simply comparing the time-of-day clock with a
710 * per-stream deadline. The latter could result in anomalies when an
711 * application does lengthy processing between write operations. Keep in
712 * mind that a receiver may not be able to keep up when a sender suddenly
713 * floods it with a lot of data as it tries to catch up with a deadline.
714 */
715 for (data = (void *) bp->data, len = to_flush; len > 0; len -= n, data += n) {
716 if (bp->flags & VSTREAM_FLAG_DEADLINE) {
717 timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0);
718 if (timeout <= 0) {
719 bp->flags |= (VSTREAM_FLAG_WR_ERR | VSTREAM_FLAG_WR_TIMEOUT);
720 errno = ETIMEDOUT;
721 return (VSTREAM_EOF);
722 }
723 if (len == to_flush)
724 GETTIMEOFDAY(&before);
725 else
726 before = stream->iotime;
727 } else
728 timeout = stream->timeout;
729 if ((n = stream->write_fn(stream->fd, data, len, timeout, stream->context)) <= 0) {
730 bp->flags |= VSTREAM_FLAG_WR_ERR;
731 if (errno == ETIMEDOUT) {
732 bp->flags |= VSTREAM_FLAG_WR_TIMEOUT;
733 stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
734 }
735 return (VSTREAM_EOF);
736 }
737 if (timeout) {
738 GETTIMEOFDAY(&stream->iotime);
739 if (bp->flags & VSTREAM_FLAG_DEADLINE) {
740 VSTREAM_SUB_TIME(elapsed, stream->iotime, before);
741 VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed);
742 }
743 }
744 if (msg_verbose > 2 && stream != VSTREAM_ERR && n != to_flush)
745 msg_info("%s: %d flushed %ld/%ld", myname, stream->fd,
746 (long) n, (long) to_flush);
747 }
748 if (bp->flags & VSTREAM_FLAG_SEEK)
749 stream->offset += to_flush;
750
751 /*
752 * Allow for partial buffer flush requests. We use memcpy() for reasons
753 * of portability to pre-ANSI environments (SunOS 4.x or Ultrix 4.x :-).
754 * This is OK because we have already verified that the to_flush count is
755 * larger than the left_over count.
756 */
757 if (left_over > 0)
758 memcpy(bp->data, bp->data + to_flush, left_over);
759 bp->cnt += to_flush;
760 bp->ptr -= to_flush;
761 return ((bp->flags & VSTREAM_FLAG_ERR) ? VSTREAM_EOF : 0);
762}
763
764/* vstream_fflush_delayed - delayed stream flush for double-buffered stream */
765
766static int vstream_fflush_delayed(VSTREAM *stream)
767{
768 int status;
769
770 /*
771 * Sanity check.
772 */
773 if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE) != VSTREAM_FLAG_READ_DOUBLE)
774 msg_panic("vstream_fflush_delayed: bad flags");
775
776 /*
777 * Temporarily swap buffers and flush unwritten data. This may seem like
778 * a lot of work, but it's peanuts compared to the write(2) call that we
779 * already have avoided. For example, delayed flush is never used on a
780 * non-pipelined SMTP connection.
781 */
782 stream->buf.flags &= ~VSTREAM_FLAG_READ;
783 VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
784 stream->buf.flags |= VSTREAM_FLAG_WRITE;
785 VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
786
787 status = VSTREAM_FFLUSH_SOME(stream);
788
789 stream->buf.flags &= ~VSTREAM_FLAG_WRITE;
790 VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
791 stream->buf.flags |= VSTREAM_FLAG_READ;
792 VSTREAM_RESTORE_STATE(stream, read_buf, read_fd);
793
794 return (status);
795}
796
797/* vstream_buf_get_ready - vbuf callback to make buffer ready for reading */
798
799static int vstream_buf_get_ready(VBUF *bp)
800{
801 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
802 const char *myname = "vstream_buf_get_ready";
803 ssize_t n;
804 struct timeval before;
805 struct timeval elapsed;
806 int timeout;
807
808 /*
809 * Detect a change of I/O direction or position. If so, flush any
810 * unwritten output immediately when the stream is single-buffered, or
811 * when the stream is double-buffered and the read buffer is empty.
812 */
813 switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
814 case VSTREAM_FLAG_WRITE: /* change direction */
815 if (bp->ptr > bp->data)
816 if ((bp->flags & VSTREAM_FLAG_DOUBLE) == 0
817 || stream->read_buf.cnt >= 0)
818 if (VSTREAM_FFLUSH_SOME(stream))
819 return (VSTREAM_EOF);
820 bp->flags &= ~VSTREAM_FLAG_WRITE;
821 if (bp->flags & VSTREAM_FLAG_DOUBLE)
822 VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
823 /* FALLTHROUGH */
824 case 0: /* change position */
825 bp->flags |= VSTREAM_FLAG_READ;
826 if (bp->flags & VSTREAM_FLAG_DOUBLE) {
827 VSTREAM_RESTORE_STATE(stream, read_buf, read_fd);
828 if (bp->cnt < 0)
829 return (0);
830 }
831 /* FALLTHROUGH */
832 case VSTREAM_FLAG_READ: /* no change */
833 break;
834 case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
835 msg_panic("%s: read/write stream", myname);
836 }
837
838 /*
839 * If this is the first GET operation, allocate a buffer. Late buffer
840 * allocation gives the application a chance to override the default
841 * buffering policy.
842 *
843 * XXX Subtle code to set the preferred buffer size as late as possible.
844 */
845 if (stream->req_bufsize == 0)
846 stream->req_bufsize = VSTREAM_BUFSIZE;
847 if (bp->len < stream->req_bufsize)
848 vstream_buf_alloc(bp, stream->req_bufsize);
849
850 /*
851 * If the stream is double-buffered and the write buffer is not empty,
852 * this is the time to flush the write buffer. Delayed flushes reduce
853 * system call overhead, and on TCP sockets, avoid triggering Nagle's
854 * algorithm.
855 */
856 if ((bp->flags & VSTREAM_FLAG_DOUBLE)
857 && stream->write_buf.len > stream->write_buf.cnt)
858 if (vstream_fflush_delayed(stream))
859 return (VSTREAM_EOF);
860
861 /*
862 * Did we receive an EOF indication?
863 */
864 if (bp->flags & VSTREAM_FLAG_EOF)
865 return (VSTREAM_EOF);
866
867 /*
868 * Fill the buffer with as much data as we can handle, or with as much
869 * data as is available right now, whichever is less. Update the cached
870 * file seek position, if any.
871 *
872 * When deadlines are enabled, we count the elapsed time for each read
873 * operation instead of simply comparing the time-of-day clock with a
874 * per-stream deadline. The latter could result in anomalies when an
875 * application does lengthy processing between read operations. Keep in
876 * mind that a sender may get blocked, and may not be able to keep up
877 * when a receiver suddenly wants to read a lot of data as it tries to
878 * catch up with a deadline.
879 */
880 if (bp->flags & VSTREAM_FLAG_DEADLINE) {
881 timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0);
882 if (timeout <= 0) {
883 bp->flags |= (VSTREAM_FLAG_RD_ERR | VSTREAM_FLAG_RD_TIMEOUT);
884 errno = ETIMEDOUT;
885 return (VSTREAM_EOF);
886 }
887 GETTIMEOFDAY(&before);
888 } else
889 timeout = stream->timeout;
890 switch (n = stream->read_fn(stream->fd, bp->data, bp->len, timeout, stream->context)) {
891 case -1:
892 bp->flags |= VSTREAM_FLAG_RD_ERR;
893 if (errno == ETIMEDOUT) {
894 bp->flags |= VSTREAM_FLAG_RD_TIMEOUT;
895 stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
896 }
897 return (VSTREAM_EOF);
898 case 0:
899 bp->flags |= VSTREAM_FLAG_EOF;
900 return (VSTREAM_EOF);
901 default:
902 if (timeout) {
903 GETTIMEOFDAY(&stream->iotime);
904 if (bp->flags & VSTREAM_FLAG_DEADLINE) {
905 VSTREAM_SUB_TIME(elapsed, stream->iotime, before);
906 VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed);
907 }
908 }
909 if (msg_verbose > 2)
910 msg_info("%s: fd %d got %ld", myname, stream->fd, (long) n);
911 bp->cnt = -n;
912 bp->ptr = bp->data;
913 if (bp->flags & VSTREAM_FLAG_SEEK)
914 stream->offset += n;
915 return (0);
916 }
917}
918
919/* vstream_buf_put_ready - vbuf callback to make buffer ready for writing */
920
921static int vstream_buf_put_ready(VBUF *bp)
922{
923 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
924 const char *myname = "vstream_buf_put_ready";
925
926 /*
927 * Sanity checks. Detect a change of I/O direction or position. If so,
928 * discard unread input, and reset the buffer to the beginning.
929 */
930 switch (bp->flags & (VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ)) {
931 case VSTREAM_FLAG_READ: /* change direction */
932 bp->flags &= ~VSTREAM_FLAG_READ;
933 if (bp->flags & VSTREAM_FLAG_DOUBLE)
934 VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
935 /* FALLTHROUGH */
936 case 0: /* change position */
937 bp->flags |= VSTREAM_FLAG_WRITE;
938 if (bp->flags & VSTREAM_FLAG_DOUBLE)
939 VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
940 else
941 VSTREAM_BUF_AT_START(bp);
942 /* FALLTHROUGH */
943 case VSTREAM_FLAG_WRITE: /* no change */
944 break;
945 case VSTREAM_FLAG_WRITE | VSTREAM_FLAG_READ:
946 msg_panic("%s: read/write stream", myname);
947 }
948
949 /*
950 * Remember the direction. If this is the first PUT operation for this
951 * stream or if the buffer is smaller than the requested size, allocate a
952 * new buffer; obviously there is no data to be flushed yet. Otherwise,
953 * flush the buffer.
954 *
955 * XXX Subtle code to set the preferred buffer size as late as possible.
956 */
957 if (stream->req_bufsize == 0)
958 stream->req_bufsize = VSTREAM_BUFSIZE;
959 if (bp->len < stream->req_bufsize) {
960 vstream_buf_alloc(bp, stream->req_bufsize);
961 } else if (bp->cnt <= 0) {
962 if (VSTREAM_FFLUSH_SOME(stream))
963 return (VSTREAM_EOF);
964 }
965 return (0);
966}
967
968/* vstream_buf_space - reserve space ahead of time */
969
970static int vstream_buf_space(VBUF *bp, ssize_t want)
971{
972 VSTREAM *stream = VBUF_TO_APPL(bp, VSTREAM, buf);
973 ssize_t used;
974 ssize_t incr;
975 ssize_t shortage;
976 const char *myname = "vstream_buf_space";
977
978 /*
979 * Sanity checks. Reserving space implies writing. It is illegal to write
980 * to a read-only stream. Detect a change of I/O direction or position.
981 * If so, reset the buffer to the beginning.
982 */
983 if (bp->put_ready == 0)
984 msg_panic("%s: read-only stream", myname);
985 switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) {
986 case VSTREAM_FLAG_READ: /* change direction */
987 bp->flags &= ~VSTREAM_FLAG_READ;
988 if (bp->flags & VSTREAM_FLAG_DOUBLE)
989 VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
990 /* FALLTHROUGH */
991 case 0: /* change position */
992 bp->flags |= VSTREAM_FLAG_WRITE;
993 if (bp->flags & VSTREAM_FLAG_DOUBLE)
994 VSTREAM_RESTORE_STATE(stream, write_buf, write_fd);
995 else
996 VSTREAM_BUF_AT_START(bp);
997 /* FALLTHROUGH */
998 case VSTREAM_FLAG_WRITE: /* no change */
999 break;
1000 case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
1001 msg_panic("%s: read/write stream", myname);
1002 }
1003
1004 /*
1005 * See if enough space is available. If not, flush a multiple of
1006 * VSTREAM_BUFSIZE bytes and resize the buffer to a multiple of
1007 * VSTREAM_BUFSIZE. We flush multiples of VSTREAM_BUFSIZE in an attempt
1008 * to keep file updates block-aligned for better performance.
1009 *
1010 * XXX Subtle code to set the preferred buffer size as late as possible.
1011 */
1012#define VSTREAM_TRUNCATE(count, base) (((count) / (base)) * (base))
1013#define VSTREAM_ROUNDUP(count, base) VSTREAM_TRUNCATE(count + base - 1, base)
1014
1015 if (stream->req_bufsize == 0)
1016 stream->req_bufsize = VSTREAM_BUFSIZE;
1017 if (want > bp->cnt) {
1018 if ((used = bp->len - bp->cnt) > stream->req_bufsize)
1019 if (vstream_fflush_some(stream, VSTREAM_TRUNCATE(used, stream->req_bufsize)))
1020 return (VSTREAM_EOF);
1021 if ((shortage = (want - bp->cnt)) > 0) {
1022 if ((bp->flags & VSTREAM_FLAG_FIXED)
1023 || shortage > __MAXINT__(ssize_t) -bp->len - stream->req_bufsize) {
1024 bp->flags |= VSTREAM_FLAG_WR_ERR;
1025 } else {
1026 incr = VSTREAM_ROUNDUP(shortage, stream->req_bufsize);
1027 vstream_buf_alloc(bp, bp->len + incr);
1028 }
1029 }
1030 }
1031 return (vstream_ferror(stream) ? VSTREAM_EOF : 0); /* mmap() may fail */
1032}
1033
1034/* vstream_fpurge - discard unread or unwritten content */
1035
1036int vstream_fpurge(VSTREAM *stream, int direction)
1037{
1038 const char *myname = "vstream_fpurge";
1039 VBUF *bp = &stream->buf;
1040
1041#define VSTREAM_MAYBE_PURGE_WRITE(d, b) if ((d) & VSTREAM_PURGE_WRITE) \
1042 VSTREAM_BUF_AT_START((b))
1043#define VSTREAM_MAYBE_PURGE_READ(d, b) if ((d) & VSTREAM_PURGE_READ) \
1044 VSTREAM_BUF_AT_END((b))
1045
1046 /*
1047 * To discard all unread contents, position the read buffer at its end,
1048 * so that we skip over any unread data, and so that the next read
1049 * operation will refill the buffer.
1050 *
1051 * To discard all unwritten content, position the write buffer at its
1052 * beginning, so that the next write operation clobbers any unwritten
1053 * data.
1054 */
1055 switch (bp->flags & (VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE)) {
1056 case VSTREAM_FLAG_READ_DOUBLE:
1057 VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf);
1058 /* FALLTHROUGH */
1059 case VSTREAM_FLAG_READ:
1060 VSTREAM_MAYBE_PURGE_READ(direction, bp);
1061 break;
1062 case VSTREAM_FLAG_DOUBLE:
1063 VSTREAM_MAYBE_PURGE_WRITE(direction, &stream->write_buf);
1064 VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf);
1065 break;
1066 case VSTREAM_FLAG_WRITE_DOUBLE:
1067 VSTREAM_MAYBE_PURGE_READ(direction, &stream->read_buf);
1068 /* FALLTHROUGH */
1069 case VSTREAM_FLAG_WRITE:
1070 VSTREAM_MAYBE_PURGE_WRITE(direction, bp);
1071 break;
1072 case VSTREAM_FLAG_READ_DOUBLE | VSTREAM_FLAG_WRITE:
1073 case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
1074 msg_panic("%s: read/write stream", myname);
1075 }
1076
1077 /*
1078 * Invalidate the cached file seek position.
1079 */
1080 bp->flags &= ~VSTREAM_FLAG_SEEK;
1081 stream->offset = 0;
1082
1083 return (0);
1084}
1085
1086/* vstream_fseek - change I/O position */
1087
1088off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence)
1089{
1090 const char *myname = "vstream_fseek";
1091 VBUF *bp = &stream->buf;
1092
1093 /*
1094 * Flush any unwritten output. Discard any unread input. Position the
1095 * buffer at the end, so that the next GET or PUT operation triggers a
1096 * buffer boundary action.
1097 */
1098 switch (bp->flags & (VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE)) {
1099 case VSTREAM_FLAG_WRITE:
1100 if (bp->ptr > bp->data) {
1101 if (whence == SEEK_CUR)
1102 offset += (bp->ptr - bp->data); /* add unwritten data */
1103 else if (whence == SEEK_END)
1104 bp->flags &= ~VSTREAM_FLAG_SEEK;
1105 if (VSTREAM_FFLUSH_SOME(stream))
1106 return (-1);
1107 }
1108 VSTREAM_BUF_AT_END(bp);
1109 break;
1110 case VSTREAM_FLAG_READ:
1111 if (whence == SEEK_CUR)
1112 offset += bp->cnt; /* subtract unread data */
1113 else if (whence == SEEK_END)
1114 bp->flags &= ~VSTREAM_FLAG_SEEK;
1115 /* FALLTHROUGH */
1116 case 0:
1117 VSTREAM_BUF_AT_END(bp);
1118 break;
1119 case VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE:
1120 msg_panic("%s: read/write stream", myname);
1121 }
1122
1123 /*
1124 * Clear the read/write flags to inform the buffer boundary action
1125 * routines that we may have changed I/O position.
1126 */
1127 bp->flags &= ~(VSTREAM_FLAG_READ | VSTREAM_FLAG_WRITE);
1128
1129 /*
1130 * Shave an unnecessary system call.
1131 */
1132 if (bp->flags & VSTREAM_FLAG_NSEEK) {
1133 errno = ESPIPE;
1134 return (-1);
1135 }
1136
1137 /*
1138 * Update the cached file seek position.
1139 */
1140 if ((stream->offset = lseek(stream->fd, offset, whence)) < 0) {
1141 if (errno == ESPIPE)
1142 bp->flags |= VSTREAM_FLAG_NSEEK;
1143 } else {
1144 bp->flags |= VSTREAM_FLAG_SEEK;
1145 }
1146 bp->flags &= ~VSTREAM_FLAG_EOF;
1147 return (stream->offset);
1148}
1149
1150/* vstream_ftell - return file offset */
1151
1152off_t vstream_ftell(VSTREAM *stream)
1153{
1154 VBUF *bp = &stream->buf;
1155
1156 /*
1157 * Shave an unnecessary syscall.
1158 */
1159 if (bp->flags & VSTREAM_FLAG_NSEEK) {
1160 errno = ESPIPE;
1161 return (-1);
1162 }
1163
1164 /*
1165 * Use the cached file offset when available. This is the offset after
1166 * the last read, write or seek operation.
1167 */
1168 if ((bp->flags & VSTREAM_FLAG_SEEK) == 0) {
1169 if ((stream->offset = lseek(stream->fd, (off_t) 0, SEEK_CUR)) < 0) {
1170 bp->flags |= VSTREAM_FLAG_NSEEK;
1171 return (-1);
1172 }
1173 bp->flags |= VSTREAM_FLAG_SEEK;
1174 }
1175
1176 /*
1177 * If this is a read buffer, subtract the number of unread bytes from the
1178 * cached offset. Remember that read counts are negative.
1179 */
1180 if (bp->flags & VSTREAM_FLAG_READ)
1181 return (stream->offset + bp->cnt);
1182
1183 /*
1184 * If this is a write buffer, add the number of unwritten bytes to the
1185 * cached offset.
1186 */
1187 if (bp->flags & VSTREAM_FLAG_WRITE)
1188 return (stream->offset + (bp->ptr - bp->data));
1189
1190 /*
1191 * Apparently, this is a new buffer, or a buffer after seek, so there is
1192 * no need to account for unread or unwritten data.
1193 */
1194 return (stream->offset);
1195}
1196
1197/* vstream_fdopen - add buffering to pre-opened stream */
1198
1199VSTREAM *vstream_fdopen(int fd, int flags)
1200{
1201 VSTREAM *stream;
1202
1203 /*
1204 * Sanity check.
1205 */
1206 if (fd < 0)
1207 msg_panic("vstream_fdopen: bad file %d", fd);
1208
1209 /*
1210 * Initialize buffers etc. but do as little as possible. Late buffer
1211 * allocation etc. gives the application a chance to override default
1212 * policies. Either this, or the vstream*open() routines would have to
1213 * have a really ugly interface with lots of mostly-unused arguments (can
1214 * you say VMS?).
1215 */
1216 stream = (VSTREAM *) mymalloc(sizeof(*stream));
1217 stream->fd = fd;
1218 stream->read_fn = VSTREAM_CAN_READ(flags) ? (VSTREAM_RW_FN) timed_read : 0;
1219 stream->write_fn = VSTREAM_CAN_WRITE(flags) ? (VSTREAM_RW_FN) timed_write : 0;
1220 vstream_buf_init(&stream->buf, flags);
1221 stream->offset = 0;
1222 stream->path = 0;
1223 stream->pid = 0;
1224 stream->waitpid_fn = 0;
1225 stream->timeout = 0;
1226 stream->context = 0;
1227 stream->jbuf = 0;
1228 stream->iotime.tv_sec = stream->iotime.tv_usec = 0;
1229 stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0;
1230 stream->req_bufsize = 0;
1231 return (stream);
1232}
1233
1234/* vstream_fopen - open buffered file stream */
1235
1236VSTREAM *vstream_fopen(const char *path, int flags, mode_t mode)
1237{
1238 VSTREAM *stream;
1239 int fd;
1240
1241 if ((fd = open(path, flags, mode)) < 0) {
1242 return (0);
1243 } else {
1244 stream = vstream_fdopen(fd, flags);
1245 stream->path = mystrdup(path);
1246 return (stream);
1247 }
1248}
1249
1250/* vstream_fflush - flush write buffer */
1251
1252int vstream_fflush(VSTREAM *stream)
1253{
1254 if ((stream->buf.flags & VSTREAM_FLAG_READ_DOUBLE)
1255 == VSTREAM_FLAG_READ_DOUBLE
1256 && stream->write_buf.len > stream->write_buf.cnt)
1257 vstream_fflush_delayed(stream);
1258 return (VSTREAM_FFLUSH_SOME(stream));
1259}
1260
1261/* vstream_fclose - close buffered stream */
1262
1263int vstream_fclose(VSTREAM *stream)
1264{
1265 int err;
1266
1267 /*
1268 * NOTE: Negative file descriptors are not part of the external
1269 * interface. They are for internal use only, in order to support
1270 * vstream_fdclose() without a lot of code duplication. Applications that
1271 * rely on negative VSTREAM file descriptors will break without warning.
1272 */
1273 if (stream->pid != 0)
1274 msg_panic("vstream_fclose: stream has process");
1275 if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0 && stream->fd >= 0)
1276 vstream_fflush(stream);
1277 /* Do not remove: vstream_fdclose() depends on this error test. */
1278 err = vstream_ferror(stream);
1279 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1280 if (stream->read_fd >= 0)
1281 err |= close(stream->read_fd);
1282 if (stream->write_fd != stream->read_fd)
1283 if (stream->write_fd >= 0)
1284 err |= close(stream->write_fd);
1285 vstream_buf_wipe(&stream->read_buf);
1286 vstream_buf_wipe(&stream->write_buf);
1287 stream->buf = stream->read_buf;
1288 } else {
1289 if (stream->fd >= 0)
1290 err |= close(stream->fd);
1291 vstream_buf_wipe(&stream->buf);
1292 }
1293 if (stream->path)
1294 myfree(stream->path);
1295 if (stream->jbuf)
1296 myfree((void *) stream->jbuf);
1297 if (!VSTREAM_STATIC(stream))
1298 myfree((void *) stream);
1299 return (err ? VSTREAM_EOF : 0);
1300}
1301
1302/* vstream_fdclose - close stream, leave file(s) open */
1303
1304int vstream_fdclose(VSTREAM *stream)
1305{
1306
1307 /*
1308 * Flush unwritten output, just like vstream_fclose(). Errors are
1309 * reported by vstream_fclose().
1310 */
1311 if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0)
1312 (void) vstream_fflush(stream);
1313
1314 /*
1315 * NOTE: Negative file descriptors are not part of the external
1316 * interface. They are for internal use only, in order to support
1317 * vstream_fdclose() without a lot of code duplication. Applications that
1318 * rely on negative VSTREAM file descriptors will break without warning.
1319 */
1320 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1321 stream->fd = stream->read_fd = stream->write_fd = -1;
1322 } else {
1323 stream->fd = -1;
1324 }
1325 return (vstream_fclose(stream));
1326}
1327
1328/* vstream_printf - formatted print to stdout */
1329
1330VSTREAM *vstream_printf(const char *fmt,...)
1331{
1332 VSTREAM *stream = VSTREAM_OUT;
1333 va_list ap;
1334
1335 va_start(ap, fmt);
1336 vbuf_print(&stream->buf, fmt, ap);
1337 va_end(ap);
1338 return (stream);
1339}
1340
1341/* vstream_fprintf - formatted print to buffered stream */
1342
1343VSTREAM *vstream_fprintf(VSTREAM *stream, const char *fmt,...)
1344{
1345 va_list ap;
1346
1347 va_start(ap, fmt);
1348 vbuf_print(&stream->buf, fmt, ap);
1349 va_end(ap);
1350 return (stream);
1351}
1352
1353/* vstream_fputs - write string to stream */
1354
1355int vstream_fputs(const char *str, VSTREAM *stream)
1356{
1357 int ch;
1358
1359 while ((ch = *str++) != 0)
1360 if (VSTREAM_PUTC(ch, stream) == VSTREAM_EOF)
1361 return (VSTREAM_EOF);
1362 return (0);
1363}
1364
1365/* vstream_control - fine control */
1366
1367void vstream_control(VSTREAM *stream, int name,...)
1368{
1369 const char *myname = "vstream_control";
1370 va_list ap;
1371 int floor;
1372 int old_fd;
1373 ssize_t req_bufsize = 0;
1374 VSTREAM *stream2;
1375
1376#define SWAP(type,a,b) do { type temp = (a); (a) = (b); (b) = (temp); } while (0)
1377
1378 for (va_start(ap, name); name != VSTREAM_CTL_END; name = va_arg(ap, int)) {
1379 switch (name) {
1380 case VSTREAM_CTL_READ_FN:
1381 stream->read_fn = va_arg(ap, VSTREAM_RW_FN);
1382 break;
1383 case VSTREAM_CTL_WRITE_FN:
1384 stream->write_fn = va_arg(ap, VSTREAM_RW_FN);
1385 break;
1386 case VSTREAM_CTL_CONTEXT:
1387 stream->context = va_arg(ap, void *);
1388 break;
1389 case VSTREAM_CTL_PATH:
1390 if (stream->path)
1391 myfree(stream->path);
1392 stream->path = mystrdup(va_arg(ap, char *));
1393 break;
1394 case VSTREAM_CTL_DOUBLE:
1395 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0) {
1396 stream->buf.flags |= VSTREAM_FLAG_DOUBLE;
1397 if (stream->buf.flags & VSTREAM_FLAG_READ) {
1398 VSTREAM_SAVE_STATE(stream, read_buf, read_fd);
1399 VSTREAM_FORK_STATE(stream, write_buf, write_fd);
1400 } else {
1401 VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
1402 VSTREAM_FORK_STATE(stream, read_buf, read_fd);
1403 }
1404 }
1405 break;
1406 case VSTREAM_CTL_READ_FD:
1407 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0)
1408 msg_panic("VSTREAM_CTL_READ_FD requires double buffering");
1409 stream->read_fd = va_arg(ap, int);
1410 stream->buf.flags |= VSTREAM_FLAG_NSEEK;
1411 break;
1412 case VSTREAM_CTL_WRITE_FD:
1413 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE) == 0)
1414 msg_panic("VSTREAM_CTL_WRITE_FD requires double buffering");
1415 stream->write_fd = va_arg(ap, int);
1416 stream->buf.flags |= VSTREAM_FLAG_NSEEK;
1417 break;
1418 case VSTREAM_CTL_SWAP_FD:
1419 stream2 = va_arg(ap, VSTREAM *);
1420 if ((stream->buf.flags & VSTREAM_FLAG_DOUBLE)
1421 != (stream2->buf.flags & VSTREAM_FLAG_DOUBLE))
1422 msg_panic("VSTREAM_CTL_SWAP_FD can't swap descriptors between "
1423 "single-buffered and double-buffered streams");
1424 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1425 SWAP(int, stream->read_fd, stream2->read_fd);
1426 SWAP(int, stream->write_fd, stream2->write_fd);
1427 stream->fd = ((stream->buf.flags & VSTREAM_FLAG_WRITE) ?
1428 stream->write_fd : stream->read_fd);
1429 } else {
1430 SWAP(int, stream->fd, stream2->fd);
1431 }
1432 break;
1433 case VSTREAM_CTL_TIMEOUT:
1434 if (stream->timeout == 0)
1435 GETTIMEOFDAY(&stream->iotime);
1436 stream->timeout = va_arg(ap, int);
1437 if (stream->timeout < 0)
1438 msg_panic("%s: bad timeout %d", myname, stream->timeout);
1439 break;
1440 case VSTREAM_CTL_EXCEPT:
1441 if (stream->jbuf == 0)
1442 stream->jbuf =
1443 (VSTREAM_JMP_BUF *) mymalloc(sizeof(VSTREAM_JMP_BUF));
1444 break;
1445
1446#ifdef VSTREAM_CTL_DUPFD
1447
1448#define VSTREAM_TRY_DUPFD(backup, fd, floor) do { \
1449 if (((backup) = (fd)) < floor) { \
1450 if (((fd) = fcntl((backup), F_DUPFD, (floor))) < 0) \
1451 msg_fatal("fcntl F_DUPFD %d: %m", (floor)); \
1452 (void) close(backup); \
1453 } \
1454 } while (0)
1455
1456 case VSTREAM_CTL_DUPFD:
1457 floor = va_arg(ap, int);
1458 if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) {
1459 VSTREAM_TRY_DUPFD(old_fd, stream->read_fd, floor);
1460 if (stream->write_fd == old_fd)
1461 stream->write_fd = stream->read_fd;
1462 else
1463 VSTREAM_TRY_DUPFD(old_fd, stream->write_fd, floor);
1464 stream->fd = (stream->buf.flags & VSTREAM_FLAG_READ) ?
1465 stream->read_fd : stream->write_fd;
1466 } else {
1467 VSTREAM_TRY_DUPFD(old_fd, stream->fd, floor);
1468 }
1469 break;
1470#endif
1471
1472 /*
1473 * Postpone memory (re)allocation until the space is needed.
1474 */
1475 case VSTREAM_CTL_BUFSIZE:
1476 req_bufsize = va_arg(ap, ssize_t);
1477 /* Heuristic to detect missing (ssize_t) type cast on LP64 hosts. */
1478 if (req_bufsize < 0 || req_bufsize > INT_MAX)
1479 msg_panic("unreasonable VSTREAM_CTL_BUFSIZE request: %ld",
1480 (long) req_bufsize);
1481 if ((stream->buf.flags & VSTREAM_FLAG_FIXED) == 0
1482 && req_bufsize > stream->req_bufsize) {
1483 if (msg_verbose)
1484 msg_info("fd=%d: stream buffer size old=%ld new=%ld",
1485 vstream_fileno(stream),
1486 (long) stream->req_bufsize,
1487 (long) req_bufsize);
1488 stream->req_bufsize = req_bufsize;
1489 }
1490 break;
1491
1492 /*
1493 * Make no gettimeofday() etc. system call until we really know
1494 * that we need to do I/O. This avoids a performance hit when
1495 * sending or receiving body content one line at a time.
1496 */
1497 case VSTREAM_CTL_STOP_DEADLINE:
1498 stream->buf.flags &= ~VSTREAM_FLAG_DEADLINE;
1499 break;
1500 case VSTREAM_CTL_START_DEADLINE:
1501 if (stream->timeout <= 0)
1502 msg_panic("%s: bad timeout %d", myname, stream->timeout);
1503 stream->buf.flags |= VSTREAM_FLAG_DEADLINE;
1504 stream->time_limit.tv_sec = stream->timeout;
1505 stream->time_limit.tv_usec = 0;
1506 break;
1507 default:
1508 msg_panic("%s: bad name %d", myname, name);
1509 }
1510 }
1511 va_end(ap);
1512}
1513
1514/* vstream_vprintf - formatted print to stdout */
1515
1516VSTREAM *vstream_vprintf(const char *format, va_list ap)
1517{
1518 VSTREAM *vp = VSTREAM_OUT;
1519
1520 vbuf_print(&vp->buf, format, ap);
1521 return (vp);
1522}
1523
1524/* vstream_vfprintf - formatted print engine */
1525
1526VSTREAM *vstream_vfprintf(VSTREAM *vp, const char *format, va_list ap)
1527{
1528 vbuf_print(&vp->buf, format, ap);
1529 return (vp);
1530}
1531
1532/* vstream_bufstat - get stream buffer status */
1533
1534ssize_t vstream_bufstat(VSTREAM *vp, int command)
1535{
1536 VBUF *bp;
1537
1538 switch (command & VSTREAM_BST_MASK_DIR) {
1539 case VSTREAM_BST_FLAG_IN:
1540 if (vp->buf.flags & VSTREAM_FLAG_READ) {
1541 bp = &vp->buf;
1542 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1543 bp = &vp->read_buf;
1544 } else {
1545 bp = 0;
1546 }
1547 switch (command & ~VSTREAM_BST_MASK_DIR) {
1548 case VSTREAM_BST_FLAG_PEND:
1549 return (bp ? -bp->cnt : 0);
1550 /* Add other requests below. */
1551 }
1552 break;
1553 case VSTREAM_BST_FLAG_OUT:
1554 if (vp->buf.flags & VSTREAM_FLAG_WRITE) {
1555 bp = &vp->buf;
1556 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1557 bp = &vp->write_buf;
1558 } else {
1559 bp = 0;
1560 }
1561 switch (command & ~VSTREAM_BST_MASK_DIR) {
1562 case VSTREAM_BST_FLAG_PEND:
1563 return (bp ? bp->len - bp->cnt : 0);
1564 /* Add other requests below. */
1565 }
1566 break;
1567 }
1568 msg_panic("vstream_bufstat: unknown command: %d", command);
1569}
1570
1571#undef vstream_peek /* API binary compatibility. */
1572
1573/* vstream_peek - peek at a stream */
1574
1575ssize_t vstream_peek(VSTREAM *vp)
1576{
1577 if (vp->buf.flags & VSTREAM_FLAG_READ) {
1578 return (-vp->buf.cnt);
1579 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1580 return (-vp->read_buf.cnt);
1581 } else {
1582 return (0);
1583 }
1584}
1585
1586/* vstream_peek_data - peek at unread data */
1587
1588const char *vstream_peek_data(VSTREAM *vp)
1589{
1590 if (vp->buf.flags & VSTREAM_FLAG_READ) {
1591 return ((const char *) vp->buf.ptr);
1592 } else if (vp->buf.flags & VSTREAM_FLAG_DOUBLE) {
1593 return ((const char *) vp->read_buf.ptr);
1594 } else {
1595 return (0);
1596 }
1597}
1598
1599#ifdef TEST
1600
1601static void copy_line(ssize_t bufsize)
1602{
1603 int c;
1604
1605 vstream_control(VSTREAM_IN, VSTREAM_CTL_BUFSIZE(bufsize), VSTREAM_CTL_END);
1606 vstream_control(VSTREAM_OUT, VSTREAM_CTL_BUFSIZE(bufsize), VSTREAM_CTL_END);
1607 while ((c = VSTREAM_GETC(VSTREAM_IN)) != VSTREAM_EOF) {
1608 VSTREAM_PUTC(c, VSTREAM_OUT);
1609 if (c == '\n')
1610 break;
1611 }
1612 vstream_fflush(VSTREAM_OUT);
1613}
1614
1615static void printf_number(void)
1616{
1617 vstream_printf("%d\n", __MAXINT__(int));
1618 vstream_fflush(VSTREAM_OUT);
1619}
1620
1621 /*
1622 * Exercise some of the features.
1623 */
1624int main(int argc, char **argv)
1625{
1626
1627 /*
1628 * Test buffer expansion and shrinking. Formatted print may silently
1629 * expand the write buffer and cause multiple bytes to be written.
1630 */
1631 copy_line(1); /* one-byte read/write */
1632 copy_line(2); /* two-byte read/write */
1633 copy_line(1); /* two-byte read/write */
1634 printf_number(); /* multi-byte write */
1635 exit(0);
1636}
1637
1638#endif
1639